/*
 *	CODESPACE ++
 *	A real-time syntax highlighting javascript editor
 *
 *	Copyright (C) 2008 Francois Botman, <Franchie.brightspark@gmail.com>
 *
 *	This script is licenced under: Creative Commons Attribution-Noncommercial-Share Alike 3.0
 *	(see http://creativecommons.org/licenses/by-nc-sa/3.0/).
 *	It bascially means that it can be modified and shared, provided it is
 *	clearly attributed, and can be used in personnal/charity projects for free!
 *	(Commercial use requires prior negotiation with the author).
 *	In any case, should anything go wrong, I cannot be held responsible!
 *
 *	Based on an original idea by Fernando M.A.d.S.,  http://codepress.org/
 *
 *	-----------
 *
 *	This file contains functions that  deal with the visual selection overlay.
 *	This file only handles:
 *		- curson display/update
 *		- selection overlay
 *		- selection erasure procedures
 */

 
 
 
 
 
 
 

/*
 * This is the selector componant.
 * It is instanciated by Codespace directly.
 */
Codespace.Selection = function(editor)
{
	var cursor = editor.api.getFixedReference();
	var cursor_ref = getPosition(cursor);
	var cursor_count = 0;
	var selection = editor.api.getFixedReference();
	var self = this;
	
	cursor.style.borderLeft = '1px solid black';
	cursor.style.width = '1px';
	cursor.style.cursor = 'text';
	
	selection.style.display = 'none';
	var r1 = selection.appendChild(document.createElement('div'));
	var r2 = selection.appendChild(document.createElement('div'));
	var r3 = selection.appendChild(document.createElement('div'));
	r1.style.position = 'absolute';
	r1.className = 'selection';
	r2.style.position = 'absolute';
	r2.className = 'selection';
	r3.style.position = 'absolute';
	r3.className = 'selection';
	
	/*
	 *	Lets make this classy! The cursor will flash!
	 */
	setInterval(function() {
		cursor.style.display = (cursor_count <= 1) ? 'block' : 'none';
		cursor_count++;
		if(cursor_count >= 3) cursor_count = 1;
	}, 700);
	
	/*
	 *	This converts a line into its screen coordinates...
	 */
	function lineToCoords(caret) {
		if(!caret.obj) return {x:0, y: 0, w: 0, h: 0, span: null};
		var p = getPosition( caret.obj );
		p.x -= cursor_ref.x;
		p.y -= cursor_ref.y;
		return {x: Math.round((p.x + (caret.objOffset)*p.w/(caret.obj.text ? caret.obj.text.length : 1)) ),
				y: p.y,
				w: p.w,
				h: p.h,
				span: caret.obj};
	}
	
	this.update = function() {
		if(!editor.focus) return;
		var a = lineToCoords(editor.caret);
		cursor.style.left = a.x + 'px';
		cursor.style.top = a.y + 'px';
		if(a.h > 8) cursor.style.height = a.h + 'px';
		cursor.target = a.span;
		/*
		 *	Set up a redirect function for when the user clicks on either the selection or the cursor
		 */
		cursor.lineobj = cursor.target.lineobj;
		r1.lineobj = r2.lineobj = r3.lineobj = cursor.lineobj;
		
		// Prevent the cursor from flashing a bit longer when it has just moved...
		cursor_count = 0;
		cursor.style.display = 'block';
		
		// And add "selected_row" class if necessary
		if(self.old_line != editor.caret.obj.lineobj.line) {
			if(self.old_line != null) editor.table.rows[self.old_line].className = editor.table.rows[self.old_line].className.replace(/selected_row/gi, '');
			editor.table.rows[editor.caret.obj.lineobj.line].className += ' selected_row';
			self.old_line = editor.caret.obj.lineobj.line;
		}
		
		// And now for the selection cursor!
		// Hide the selection if both cursors are at the same place!
		if(editor.caret.obj == editor.selectCaret.obj && editor.caret.objOffset == editor.selectCaret.objOffset) {selection.style.display = 'none';makeVisible(cursor);return;}
		
		selection.style.display = 'block';
		var a = lineToCoords(editor.caret);
		var b = lineToCoords(editor.selectCaret);
		var c = a.span.parentNode;
		
		selection.is_selection = r1.is_selection = r2.is_selection = r3.is_selection = true;
		if(Math.abs(a.y-b.y)<a.h) r1.target = a.span;
		drawSelection(a.x, a.y, b.x, b.y, a.h, c.offsetWidth, c.offsetLeft);
	}

	/*
	 *	This draws the selection box...
	 */
	function drawSelection(startx, starty, endx, endy, rowHeight, rowWidth, rowx) {
		// Either a single-row selection (starts and ends on same line), no fuss, its easy.
		if(Math.abs(endy-starty)<rowHeight) {
			r1.style.display = 'block';
			r2.style.display = 'none';
			r3.style.display = 'none';
			r1.style.height = rowHeight + 'px';
			r1.style.left = Math.min(startx, endx) + 'px';
			r1.style.width = Math.abs(startx - endx) + 'px';
			r1.style.top = starty + 'px';
			makeVisible(r1, (startx < endx));
		}
		
		// Or its a multiline selection -- we need to handle aesthetic cases (empty new line, etc).
		else {
			var x = (starty<endy) ? startx : endx;
			var y = (starty<endy) ? starty : endy;
			r1.style.display = 'block';
			r1.style.height = rowHeight + 'px';
			r1.style.left = x + 'px';
			r1.style.width = (rowWidth - x + rowx) + 'px';
			r1.style.top = y + 'px';
			if(Math.abs(endy-starty) > rowHeight) {
				r2.style.display = 'block';
				r2.style.height = (Math.abs(endy-starty) - rowHeight) + 'px';
				r2.style.left = rowx + 'px';
				r2.style.width = (rowWidth) + 'px';
				r2.style.top = (y+rowHeight) + 'px';
			}
			else r2.style.display = 'none';
			r3.style.display = 'block';
			r3.style.height = rowHeight + 'px';
			r3.style.left = rowx + 'px';
			r3.style.width = Math.max((((starty<endy) ? endx : startx) - rowx), (starty<endy ? 10 : 0)) + 'px';
			r3.style.top = Math.max(starty, endy) + 'px';
			makeVisible((starty > endy) ? r1 : r3, (starty < endy));
		}
	}
	
	
	// And this moves the selection on drag...
	function onmousedrag(evt) {
		evt = evt || window.event;
		if(!editor.allow_dragging) return;
		editor.process_mouse_up = true;
		
		selection.style.display = 'block';
		
		/*
		 *	This horrible bit gets the mouse coords relative to the page in a (hopefully) cross-browser fashion.
		 */
		var mouse = {x:0, y:0};
		if(evt.pageX || evt.pageY) {mouse.x = evt.pageX;mouse.y = evt.pageY;}
		else if(evt.clientX || evt.clientY) {
			mouse.x = evt.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
			mouse.y = evt.clientY + document.body.scrollTop	+ document.documentElement.scrollTop;
		}
		
		var a = lineToCoords(editor.caret);
		var c = editor.caret.obj.parentNode;
		
		/*
		 *	Unfortunately, there is a problem:
		 *	We need to have an event sent from a daughter node to determine the position of the click.
		 *	But in this case the selection is in front of it!!
		*	... so we need to ensure it is NOT in front of the cursor!
		*
		*	Next we need to display the selection as the mouse moves. As it is too costly to calculate exactly where
		*	the user is respective to the text, we simply snap to a multiple of the average line height. This is
		*	obviously not perfect, but it is fast, and early tests have shown good results.
		*/
		var delta = (a.y>(mouse.y-cursor_ref.y)) ? +1 : -2;
		var y = a.y + Math.floor((mouse.y-cursor_ref.y-a.y)/a.h)*a.h;
		drawSelection(a.x, a.y, mouse.x-cursor_ref.x+delta, y, a.h, c.offsetWidth, c.offsetLeft);
		
		// For IE
		return false;
	}
	
	if(editor.table.attachEvent) editor.table.attachEvent('onmousemove', onmousedrag);
	else editor.table.addEventListener('mousemove', onmousedrag, false);
	
	
	/*
	 *	Sometimes the window is resized, and the reference point is no longer where we think it is.
	 *	Thus occasionnally, we need to update its position...
	 */
	function cresize() {cursor_ref = getPosition(editor.fixed_reference_div);};
	if(window.attachEvent) window.attachEvent('onresize', cresize);
	else window.addEventListener('resize', cresize, false);
	
	selection.onmousemove = onmousedrag;
	selection.onmousedown = function(){return false;}
	
	editor.registerHandler('click', cresize);
}
 
 
 
